﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace IconMerge
{
    public partial class MainWnd : Form
    {
        public MainWnd()
        {
            InitializeComponent();
        }

        /*
         * ICO文件格式
         * 最多8个图像，最大256x256
         * https://docs.fileformat.com/zh/image/ico/
         * ICO文件
         *      文件头（6字节）
         *         2：保留：必须是0
         *         2：图像类型：1-图标 0-光标
         *         2：图标中的图片数量
         *      图片目录（16*n字节，16字节为图片基础信息，n为图片数量）
         *         1：像素宽度，如果是256像素，则为0
         *         1：像素高度，如果是256像素，则为0
         *         1：位深度（PixelFormat）,如果超过256，则为0
         *         1：保留，为0
         *         2：.ICO 格式时的颜色平面，应为 0 或 1，或 .CUR 格式时的 X 热点
         *         2：.ICO 格式时每像素的位数，或 .CUR 格式时的 Y 热点
         *         4：图片的字节数大小
         *         4：图片在图标文件中的偏移量
         *      图片数据（每个图片的字节数据）
         *         图片1
         *         ······
         *         图片n
        */

        /// <summary>
        /// 加载的图片
        /// </summary>
        List<ImageFile> imageFile = new List<ImageFile>();

        /// <summary>
        /// 图片文件类
        /// </summary>
        public class ImageFile
        {
            /// <summary>
            /// 图片宽度
            /// </summary>
            public int Width { get; set; }

            /// <summary>
            /// 图片高度
            /// </summary>
            public int Height { get; set; }

            /// <summary>
            /// 图片尺寸（宽*高）
            /// </summary>
            public string Size { get; set; }

            /// <summary>
            /// 图片字节数
            /// </summary>
            public int Length { get; set; }

            /// <summary>
            /// 图片路径
            /// </summary>
            public string Path { get; set; }

            /// <summary>
            /// 图片Image对象
            /// </summary>
            public Image Img { get; set; }

            /// <summary>
            /// 图片字节数据（字节数组）
            /// </summary>
            public byte[] Data { get; set; }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="path"></param>
            public ImageFile(string path)
            {
                Img = Image.FromFile(path);
                Data = ToBytes(Img);
                Length = Data.Length;
                Width = Img.Width;
                Height = Img.Height; 
                Size = $"{Img.Size.Width}*{Img.Size.Height}" ;
                Path = path;
            }

            /// <summary>
            /// 将图片转换为字节数组
            /// </summary>
            /// <param name="image"></param>
            /// <returns></returns>
            private byte[] ToBytes(Image image)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    image.Save(ms, image.RawFormat);
                    return ms.ToArray();
                }
            }
        }

        
        /// <summary>
        /// 加载图片按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLoad_Click(object sender, EventArgs e)
        {
            imageFile.Clear();
            if (ofdSource.ShowDialog() == DialogResult.OK)
            {
                if (ofdSource.FileNames.Length > 8)
                {
                    MessageBox.Show("不能超过8个文件");
                    return;
                }
                foreach (string file in ofdSource.FileNames)
                {
                    ImageFile img = new ImageFile(file);
                    imageFile.Add(img);
                }
            }
            dgvSource.AutoGenerateColumns =false;
            dgvSource.DataSource = imageFile;
        }

        /// <summary>
        /// 合并图片
        /// </summary>
        /// <param name="path"></param>
        public void Merge(string path)
        {
            // 创建目标图标文件
            using (Stream stream = new FileStream(path, FileMode.Create))
            {
                // 把加载的图片按照尺寸排序
                imageFile.OrderByDescending(x => x.Width).ThenByDescending(x => x.Height);
                // 创建二级制writer
                using (BinaryWriter bw = new BinaryWriter(stream))
                {
                    try
                    {
                        // 图标文件头长度，6字节
                        int headerLength = 6;
                        // 图标文件目录中，每个图片基础信息的长度，16字节
                        int dirLength = 16;
                        // 图标文件中，每个图片在图标文件的字节偏移量
                        uint offSet = 0;

                        /* 文件头：存储有关 ICO 文件的一般信息，6字节。 */
                        // 保留，2字节，必须是0
                        bw.Write((ushort)0);
                        // 图像类型，2字节，1表示图标，2表示光标
                        bw.Write((ushort)1);
                        // 图标中的图像数量，2字节
                        bw.Write((ushort)imageFile.Count);
                        // 偏移量的初始值（第一个图片的偏移量）
                        offSet = (uint)(headerLength + dirLength * imageFile.Count);
                        // 循环
                        foreach (ImageFile png in imageFile)
                        {
                            /* 存储文件中每个图像的一般信息 */
                            // 宽度，如果是 256 像素，则应为 0
                            if (png.Width == 256)
                            {
                                bw.Write((byte)0);
                            }
                            else
                            {
                                bw.Write((byte)png.Width);
                            }
                            // 高度，如果是 256 像素，则应为 0
                            if (png.Height == 256)
                            {
                                bw.Write((byte)0);
                            }
                            else
                            {
                                bw.Write((byte)png.Height);
                            }
                            // 颜色计数，如果超过 256 种颜色，则应为 0
                            int pixelFormat = Image.GetPixelFormatSize(png.Img.PixelFormat);
                            if (pixelFormat < 256)
                            {
                                bw.Write((byte)Image.GetPixelFormatSize(png.Img.PixelFormat));
                            }
                            else
                            {
                                bw.Write((byte)0);
                            }
                            // 保留，应为 0
                            bw.Write((byte)0);
                            // .ICO 格式时的颜色平面，应为 0 或 1，或 .CUR 格式时的 X 热点
                            bw.Write((ushort)1);
                            // .ICO 格式时每像素的位数，或 .CUR 格式时的 Y 热点
                            bw.Write((ushort)pixelFormat);
                            // 位图数据的大小，以字节为单位。
                            bw.Write(png.Length);
                            // 文件中的偏移量。
                            bw.Write(offSet);
                            // 修改偏移量
                            offSet += (uint)png.Length;
                        }
                        // 写入图片数据
                        foreach (ImageFile png in imageFile)
                        {
                            bw.Write(png.Data);
                        }
                        // 关闭stream，图标保存
                        stream.Close();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
                MessageBox.Show("Done");
            }      
        }

        /// <summary>
        /// 合并图标按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnMerge_Click(object sender, EventArgs e)
        {
            // 选择保存路径
            if (sfdTarget.ShowDialog() == DialogResult.OK)
            {
                string path = sfdTarget.FileName;
                Merge(path);
            }
        }
    }
}
